package com.liu.yubi.service.impl;

import static com.liu.yubi.constant.UserConstant.USER_LOGIN_STATE;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liu.yubi.common.ErrorCode;
import com.liu.yubi.constant.CommonConstant;
import com.liu.yubi.constant.RedisConstant;
import com.liu.yubi.constant.UserConstant;
import com.liu.yubi.exception.BusinessException;
import com.liu.yubi.exception.ThrowUtils;
import com.liu.yubi.mapper.UserMapper;
import com.liu.yubi.model.dto.user.UserQueryRequest;
import com.liu.yubi.model.dto.user.UserUpdateRequest;
import com.liu.yubi.model.entity.User;
import com.liu.yubi.model.enums.UserRoleEnum;
import com.liu.yubi.model.vo.LoginUserVO;
import com.liu.yubi.model.vo.UserVO;
import com.liu.yubi.service.UserService;
import com.liu.yubi.utils.CopyUtils;
import com.liu.yubi.utils.SqlUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

/**
 * 用户服务实现
 *

 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    /**
     * 盐值，混淆密码
     */
    private static final String SALT = "detail";

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 注册用户
     * @param userAccount   用户账户
     * @param userPassword  用户密码
     * @param checkPassword 校验密码
     */
    @Override
    public Long userRegister(String userAccount, String userPassword, String checkPassword) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        synchronized (userAccount.intern()) {
            // 账户不能重复
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userAccount", userAccount);
            long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
            }
            // 2. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            // 3. 插入数据
            User user = new User();
            user.setUserAccount(userAccount);
            user.setUserPassword(encryptPassword);
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
            }
            return user.getId();
        }
    }

    /**
     * 登录用户
     * @param userAccount  用户账户
     * @param userPassword 用户密码
     */
    @Override
    public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
        }
        if (userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        queryWrapper.eq("userPassword", encryptPassword);
        User user = this.baseMapper.selectOne(queryWrapper);
        // 用户不存在
        if (user == null) {
            log.info("user login failed, userAccount cannot match userPassword");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误!");
        }
        // 3. 记录用户的登录态
        request.getSession().setAttribute(USER_LOGIN_STATE, user);
        return this.getLoginUserVO(user);
    }


    /**
     * 管理员添加用户
     * @param userAddRequest 添加用户请求类
     */
    @Override
    public Boolean addUser(User userAddRequest) {
        //校验参数
        String userAccount = userAddRequest.getUserAccount ();
        String userPassword = userAddRequest.getUserPassword ();
        String userName = userAddRequest.getUserName ();
        if (StringUtils.isAnyBlank (userAccount, userPassword, userName)) {
            throw new BusinessException (ErrorCode.PARAMS_ERROR,"参数不能为空");
        }
        ThrowUtils.throwIf (userAccount.length ()>10,ErrorCode.PARAMS_ERROR,"用户名过长");
        ThrowUtils.throwIf (userName.length ()>20,ErrorCode.PARAMS_ERROR,"用户昵称过长");
        ThrowUtils.throwIf (userPassword.length ()>8,ErrorCode.PARAMS_ERROR,"密码过长");
        //规范密码
        Pattern pattern = Pattern.compile("^[a-zA-Z0-9]+$");
        ThrowUtils.throwIf (!pattern.matcher (userPassword).matches (),ErrorCode.PARAMS_ERROR,
                "密码只能由a-Z+0-9组成");

        //判断用户是否存在
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<> ();
        userQueryWrapper.eq ("userAccount",userAccount );
        User one = this.getOne (userQueryWrapper);
        if (one != null) {
            throw new BusinessException (ErrorCode.PARAMS_ERROR,"该用户已存在，请添加其他用户");
        }

        //加密密码
        String password = DigestUtils.md5DigestAsHex ((SALT + userPassword).getBytes ());
        userAddRequest.setUserPassword (password);

        //保存到数据
        boolean save = this.save (userAddRequest);
        ThrowUtils.throwIf (!save,ErrorCode.OPERATION_ERROR,"保存数据库失败");

        return true;
    }

    /**
     * 登录打卡
     */
    @Override
    public Boolean signIn(HttpServletRequest request) {
        //获取当前用户
        User loginUser = this.getLoginUser (request);
        if (loginUser == null) {
            throw new BusinessException (ErrorCode.NOT_LOGIN_ERROR);
        }
        //判断是否已经打卡
        String isSign =  stringRedisTemplate.opsForValue ().get (RedisConstant.USER_SIGN_IN_REDIS_ID + loginUser.getId ());
        if (!StringUtils.isBlank (isSign) && isSign.equals (UserConstant.USER_SIGN_IN)){
            throw new BusinessException (ErrorCode.PARAMS_ERROR,"今天已经签到，请明天再来");
        }
        //计算当前时间至次日凌晨的秒数
        LocalDateTime currentTime = LocalDateTime.now ();

        LocalDateTime nextDayTime = currentTime.plusDays (1).withHour (0)
                .withMinute (0).withSecond (0).withNano (0);
        //签到保持时间
        Duration duration = Duration.between (currentTime, nextDayTime);
        long seconds = Math.abs (duration.getSeconds ());
        //存入redis中
        try {
            stringRedisTemplate.opsForValue ().set (RedisConstant.USER_SIGN_IN_REDIS_ID+ loginUser.getId (),
                    UserConstant.USER_SIGN_IN,seconds, TimeUnit.SECONDS);
        }catch (Exception e){
            log.info ("用户{}签到失败",loginUser.getId (),e);
            throw new BusinessException (ErrorCode.SYSTEM_ERROR,"签到失败");
        }
        //修改积分
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<> ();
        userUpdateWrapper.eq ("id",loginUser.getId ());
        userUpdateWrapper.setSql ("score=score+20");
        return this.update (userUpdateWrapper);

    }

    /**
     * 扣除用户积分（每次调用AI需要积分）
     */
    @Override
    public void deductUserScore(Long userId) {
        User user = this.getById (userId);
        if (user == null) {
            throw new BusinessException (ErrorCode.NOT_LOGIN_ERROR);
        }
        //判断积分是否足够
        if (user.getScore ()-5<0){
            throw new BusinessException (ErrorCode.OPERATION_ERROR,"积分不足");
        }
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<> ();
        userUpdateWrapper.eq ("id",user.getId ());
        userUpdateWrapper.setSql ("score=score-5");
        boolean update = this.update (userUpdateWrapper);
        if (!update){
            log.error ("用户{}扣除积分失败",user.getId ());
            throw new BusinessException (ErrorCode.SYSTEM_ERROR);
        }
        log.info ("用户{}扣除积分成功",user.getId ());
    }

    /**
     * 判断用户积分积分是否足够
     */
    @Override
    public Boolean userHasScore(User user) {
        if (user == null) {
            throw new BusinessException (ErrorCode.NOT_LOGIN_ERROR);
        }
        //判断用户积分是否大于5
        Integer score = user.getScore ();
        return score!=null && score>=5;
    }

    /**
     * 获取全部用户信息
     * @param userQueryRequest 查询条件
     * @param loginUser 当期用户
     */
    @Override
    public Page<User> pageUser(UserQueryRequest userQueryRequest, User loginUser) {
        long pageSize = userQueryRequest.getPageSize ();
        long current = userQueryRequest.getCurrent ();
        //查询关键字
        String searchParams = userQueryRequest.getSearchParams ();
        // 查询条件
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<> ();
        userQueryWrapper.and (StringUtils.isNotEmpty (searchParams),
                q1->q1.like ("userRole",searchParams).
                        or (q2->q2.like ("userName",searchParams)));
        //去除自己
        userQueryWrapper.ne ("id",loginUser.getId ());
       //返回结果
       return this.page (new Page<> (current, pageSize), userQueryWrapper);
     }

    /**
     * 更新用户
     * @param userUpdateRequest 更新用户dto
     */
    @Override
    public Boolean updateUser(User userUpdateRequest,HttpServletRequest request) {
        User loginUser = this.getLoginUser (request);
        Long userId = userUpdateRequest.getId();
        User oldUser = this.getById(userId);
//        if (UserConstant.ADMIN_ROLE.equals(userUpdateRequest.getUserRole())) {
//            throw new BusinessException(ErrorCode.OPERATION_ERROR, "管理员用户不可修改");
//        }
//        if (Boolean.TRUE.equals(!this.isAdmin(loginUser)) && !loginUser.getId().equals(userId)) {
//            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限修改");
//        }
        //修改用户名不可与其他用户名一致
        if (!oldUser.getUserAccount().equals(userUpdateRequest.getUserAccount())) {
            String userAccount = userUpdateRequest.getUserAccount();
            if (this.count(new QueryWrapper<User>().eq("userAccount", userAccount)) > 0) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户名已存在");
            }
        }
        //密码不一致才更新操作
        if (StringUtils.isNotEmpty(userUpdateRequest.getUserPassword())
                && !oldUser.getUserPassword().equals(userUpdateRequest.getUserPassword())) {
            userUpdateRequest.setUserPassword(DigestUtils.md5DigestAsHex((SALT + userUpdateRequest.getUserPassword()).getBytes()));
        }
        return this.updateById(userUpdateRequest);

    }

    /**
     * 获取当前登录用户
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        currentUser = this.getById(userId);
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return currentUser;
    }

    /**
     * 获取当前登录用户（允许未登录）
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUserPermitNull(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            return null;
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        return this.getById(userId);
    }

    /**
     * 是否为管理员
     *
     * @param request
     * @return
     */
    @Override
    public Boolean isAdmin(HttpServletRequest request) {
        // 仅管理员可查询
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User user = (User) userObj;
        return isAdmin(user);
    }

    @Override
    public Boolean isAdmin(User user) {
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }

    /**
     * 用户注销
     *
     * @param request
     */
    @Override
    public Boolean userLogout(HttpServletRequest request) {
        if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        // 移除登录态
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return true;
    }

    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVO = CopyUtils.copy (user, LoginUserVO.class);
        //是否签到
        String isSign = stringRedisTemplate.opsForValue ().get (RedisConstant.USER_SIGN_IN_REDIS_ID+user.getId ());
        loginUserVO.setSignIn (StringUtils.isNotBlank (isSign) && isSign.equals (UserConstant.USER_SIGN_IN));
        //签到积分
        loginUserVO.setScore (user.getScore ());
        return loginUserVO;

    }

    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        return CopyUtils.copy (user, UserVO.class);
    }

    @Override
    public List<UserVO> getUserVO(List<User> userList) {
        if (CollectionUtils.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<> ();
        if (userQueryRequest==null){
           return userQueryWrapper;
        }

        String userAccount = userQueryRequest.getUserAccount ();
        String score = userQueryRequest.getScore ();
        Long id = userQueryRequest.getId ();
        String userName = userQueryRequest.getUserName ();
        String userRole = userQueryRequest.getUserRole ();
        String sortField = userQueryRequest.getSortField ();
        String sortOrder = userQueryRequest.getSortOrder ();
        //限制条件
        userQueryWrapper.eq (id!=null&&id>0,"id",id);
        userQueryWrapper.like (StringUtils.isNotBlank(userName),"userName",userName);
        userQueryWrapper.like (StringUtils.isNotBlank(userAccount),"userAccount",userAccount);
        userQueryWrapper.like (StringUtils.isNotBlank(userAccount),"userAccount",userAccount);
        userQueryWrapper.like (score!=null,"score",score);
        userQueryWrapper.eq (StringUtils.isNotBlank(userRole),"userRole",userRole);
        userQueryWrapper.eq ("isDelete",false);
        //排序
        userQueryWrapper.orderBy (SqlUtils.validSortField (sortField),sortOrder.equals (CommonConstant.SORT_ORDER_ASC),
                sortField);
        return userQueryWrapper;
    }

}
